﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Dtd.Schema;

namespace Engine.Sgml.Matching {
    internal static class MatchFinalizer {
        public static void Finalize(Match match) {
            Finalize(match, match.Model.FirstItem);
            }

        public static void Finalize(Match match, IModelGroupsNode startItem) {
            Guard.ArgumentNotNull(match, "match");
            switch (match.Model.OrderType) {
                case DGroupOrderType.OnlyOne:
                    FinalizeOnlyOneModel(match);
                    break;
                case DGroupOrderType.Sequence:
                    FinalizeSequenceModel(match, startItem);
                    break;
                case DGroupOrderType.AnyOrder:
                    FinalizeAnyOrderModel(match);
                    break;
                default:
                    throw new NotSupportedException("Unknown the order type of a model groups.");
                }
            }

        private static void FinalizeOnlyOneModel(Match match) {
            if (match.Type == AccordanceTypes.Inconsistent) {
                for (int i = 0; i < match.Model.Count; i++) {
                    if (match.Model[i].MustPresent() && match.Model[i].Declaration != match.Ancestor.Declaration) {
                        if (match.Model[i].IsModelGroups()) {
                            Match inner = new Match(match.Model[i].FirstItem.Parent, match.Ancestor, match);
                            Finalize(inner);
                            match.Captures.Add(inner, false);
                            }
                        else
                            match.Captures.Add(match.Model[i]);
                        return;
                        }
                    }
                for (int i = 0; i < match.Model.Count; i++) {
                    if (match.Model[i].Declaration != match.Ancestor.Declaration) {
                        if (match.Model[i].IsModelGroups()) {
                            Match inner = new Match(match.Model[i].FirstItem.Parent, match.Ancestor, match);
                            Finalize(inner);
                            match.Captures.Add(inner, false);
                            }
                        else
                            match.Captures.Add(match.Model[i]);
                        return;
                        }
                    }
                }
            }

        private static void FinalizeSequenceModel(Match match, IModelGroupsNode startItem) {
            if (startItem == null)
                return;
            IModelGroupsNode item = startItem;
            while (item != null) {
                if (item.IsModelGroups()) {
                    Match inner = new Match(item.FirstItem.Parent, match.Ancestor, match);
                    Finalize(inner);
                    match.Captures.Add(inner, false);
                    }
                else
                    match.Captures.Add(item);
                item = item.NextItem();
                }
            }

        private static void FinalizeAnyOrderModel(Match match) {
            if (match.Captures.Count == 0 && !match.Model.MustPresent())
                return;
            // Define missed elements
            for (int i = 0; i < match.Model.Count; i++) {
                IModelGroupsNode groupItem = match.Model[i];
                if (groupItem.MustPresent() && !match.Captures.Contains(groupItem)) {
                    if (groupItem.IsModelGroups()) {
                        Match inner = new Match(groupItem.FirstItem.Parent, match.Ancestor, match);
                        match.Captures.Add(inner, false);
                        }
                    else
                        match.Captures.Add(groupItem);
                    }
                }
            }
        }
    }
